package org.example.service;

import lombok.Getter;
import org.example.exception.BizException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.InputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

public class UploadService2Task {
    @Getter
    private final String uploadId;
    private final String uploadPath;
    private final Map<Integer, Boolean> uploadActiveMap;
    private final Map<Integer, String> uploadChunkMap;
    private final AtomicBoolean merging;
    private final UploadService2Handler handler;
    private final Logger logger;
    private final AtomicBoolean cancel;

    public UploadService2Task(String uploadPath, UploadService2Handler handler) {
        this.uploadId = UUID.randomUUID().toString();
        this.uploadPath = uploadPath;
        this.uploadActiveMap = new ConcurrentHashMap<>();
        this.merging = new AtomicBoolean(false);
        this.handler = handler;
        this.uploadChunkMap = new ConcurrentHashMap<>();
        this.cancel = new AtomicBoolean(false);
        this.logger = LoggerFactory.getLogger("UploadService2Task-" + uploadId);
    }

    private void doUploadChunk(String chunkPath, InputStream inputStream) {
        handler.saveChunk(chunkPath, inputStream);
    }

    public void uploadChunk(int chunkIndex, InputStream inputStream) {
        if (cancel.get()) {
            logger.error("任务{}被取消，上传分片失败", uploadId);
            throw new BizException(String.format("任务%s被取消，上传分片失败", uploadId), null);
        }
        if (merging.get()) {
            logger.error("任务{}的上传分片{}失败，正在合并中", uploadId, chunkIndex);
            throw new BizException(String.format("任务%s的上传分片%d失败，正在合并中", uploadId, chunkIndex), null);
        }
        Boolean uploadActivation = uploadActiveMap.putIfAbsent(chunkIndex, true);
        if (uploadActivation != null) {
            logger.error("任务{}的分片{}正在上传中，上传分片失败", uploadId, chunkIndex);
            throw new BizException(String.format("任务%s的分片%d正在上传中，上传分片失败", uploadId, chunkIndex), null);
        }
        try {
            String path = Paths.get(uploadPath, String.valueOf(chunkIndex)).toString();
            doUploadChunk(path, inputStream);
            uploadChunkMap.put(chunkIndex, path);
        } finally {
            uploadActiveMap.remove(chunkIndex);
        }
    }

    private void doMerge(Runnable onFinished) {
        Thread thread = new Thread(() -> {
            List<String> chunkFiles = uploadChunkMap.entrySet()
                    .stream()
                    .sorted(Map.Entry.comparingByKey())
                    .map(Map.Entry::getValue)
                    .collect(Collectors.toList());
            Path mergedPath = Paths.get(uploadPath, uploadId);
            handler.merge(chunkFiles, mergedPath.toString(), cancel, onFinished);
        });
        thread.start();


//        Path uploadPathObj = Paths.get(uploadPath);
//        try (
//                Stream<Path> pathStream = Files.list(uploadPathObj)
//        ) {
//            List<String> chunkFiles = pathStream.map(Path::toString).collect(Collectors.toList());
//            Path mergedPath = Paths.get(uploadPath, uploadId);
//            handler.merge(chunkFiles, mergedPath.toString(), cancel, () -> {
//                this.merging.set(false);
//                if (cancel.get()) {
//                    clearResource();
//                }
//            });
//        } catch (Exception e) {
//            throw new BizException(String.format("任务%s合并失败", uploadId), e);
//        }
    }

    public void merge() {
        if (cancel.get()) {
            logger.error("任务{}被取消，合并失败", uploadId);
            throw new BizException(String.format("任务%s被取消，合并失败", uploadId), null);
        }
        if (merging.get()) {
            logger.error("任务{}合并失败，已在合并中", uploadId);
            throw new BizException(String.format("任务%s合并失败，已在合并中", uploadId), null);
        }
        if (!uploadActiveMap.isEmpty()) {
            logger.error("任务{}合并失败，有分片正在上传", uploadId);
            throw new BizException(String.format("任务%s合并失败，有分片正在上传", uploadId), null);
        }
        if (!merging.compareAndSet(false, true)) {
            logger.error("任务{}合并失败，已在合并中", uploadId);
            throw new BizException(String.format("任务%s合并失败，已在合并中", uploadId), null);
        }
        doMerge(() -> {
            this.merging.set(false);
            if (cancel.get()) {
                clearResource();
            }
        });
    }

    private void clearResource() {
        List<String> chunkFiles = new ArrayList<>(uploadChunkMap.values());
        Path mergedPath = Paths.get(uploadPath, uploadId);
        chunkFiles.add(mergedPath.toString());
        handler.clean(chunkFiles);
    }

    public void cancel() {
        this.cancel.set(true);
    }
}
